/*
 * Copyright (c) 2007 Thomas Fors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// =======================================================
// Interface to Adobe Camera Raw
// =======================================================


// -------------------------------------------------------
// Acr()
// Constructor
// -------------------------------------------------------
function Acr() {
	this.temp = 5000;    		// Color Temp: 2000 to 50000
	this.tint = 0;              // Tint: -150 to 150
	this.exps = 0;              // Exposure: -4.0 to +4.0
	this.shad = 5;              // Shadows: 0 to 100
	this.brgh = 50;             // Brightness: 0 to 100
	this.cntr = 25;             // Contrast: -50 to 100
	this.strt = 0;              // Saturation: -100 to 100
	this.shrp = 0;              // Sharpness: 0 to 100
	this.lnr = 0;               // Luminance Smoothing: 0 to 100
	this.cnr = 25;              // Color Noise Reduction: 0 to 100
	this.ca1 = 0;               // Chromatic Aberration (R/C): -100 to 100
	this.ca2 = 0;               // Chromatic Aberration (B/Y): -100 to 100
	this.viga = 0;              // Vignetting Amount: -100 to 100
	this.vigm = 50;             // Vignetting Midpoint: 0 to 100
	this.blkb = 0;              // Shadow Tint: -100 to 100
	this.rhue = 0;              // Red Hue: -100 to 100
	this.rsat = 0;              // Red Saturation: -100 to 100
	this.ghue = 0;              // Green Hue: -100 to 100
	this.gsat = 0;              // Green Saturation: -100 to 100
	this.bhue = 0;              // Blue Hue: -100 to 100
	this.bsat = 0;              // Blue Saturation: -100 to 100
	this.clrs = "ProP";         // Color Space
	this.btdp = "BD8 ";         // Bit Depth
	this.sz = 1024;            	// Image Size (width)  -- ACR will chose closest available
	this.rslt = 240;            // Resolution
	this.reun = "PpIn";         // Resolution Units
	
	this.nOpens = 0;			// Raw file open counter
	
	this.nOpenCalls = 0;
	this.nCloseCalls = 0;
	
	this.version = "Unknown";	// ACR Version
	this.supported = "";
}

// -------------------------------------------------------
// Initialize()
// -------------------------------------------------------
Acr.prototype['Initialize'] = function () {
	this.SetFileName(this.GetActiveDocFileName());
	this.SetCameraModel(this.GetXmpMetadata("tiff:Model"));
	this.SetSize(this.GetActiveDocWidth());
	this.SetColorTemp(this.GetXmpMetadata("crs:Temperature"));
	this.SetTint(this.GetXmpMetadata("crs:Tint"));

	// Fail if this is not the original raw file
	if (activeDocument.name != this.leftTrim(this.GetXmpMetadata("crs:RawFileName"))) {
		throw("You must start AcrCalibrator with the original raw file.\n" +
			  "Do not save the file as a psd or any other file type before\n" +
			  "running the script.");
	};

	// Fail if the raw was cropped or rotated
	if (this.leftTrim(this.GetXmpMetadata("crs:HasCrop")) == "True") {
		throw("Do not crop or rotate the raw file in ACR before running the script.");
	};
	
	this.version = this.GetXmpMetadata("crs:Version");
	this.version = this.version.replace(/[^0123456789\.x]/g, "");
	
	var supportedVersions = Array( "2.2",	 "2.4x101",	"2.4",		"3.0",
								   "3.1",	 "3.2",		"3.3x43",	"3.3",
								   "3.4x89", "3.5",		"3.6",		"3.7",
								   "4.0",    "4.1",		"4.2",		"4.3.1" );
	
	var supported = false;
	for (var i = 0; i < supportedVersions.length; i++) {
		if (supportedVersions[i] == this.version) {
			supported = true;
		} 
    }        
	
	if ( !supported ) {
		this.supported = " UNSUPPORTED";
		if ( ! confirm("Unsupported Version\n\n" + 
					 "You are running ACR version " + this.version + ".\n" +
					 "This script has not been tested with your version of ACR.\n\n" +
					 "Click:\n" +
					 "          YES to proceed anyway (your results may vary).\n" +
					 "          NO to exit.\n\n" +
					 "Visit http://chromoholics.com and check for script updates.\n\n" +
					 "Do you want to continue?") ) {
			throw("Script aborted at user request.");
		}
	}
}

// -------------------------------------------------------
// GetXmpMetadata()
// Returns the meta data labeled by tag
// -------------------------------------------------------
Acr.prototype['GetXmpMetadata'] = function (tag) {
	var i, metadata, result;
	
	var reFind  = new RegExp("<" + tag + ">.*</" + tag + ">");
	var reStart = new RegExp("<" + tag + ">");
	var reEnd   = new RegExp("</" + tag + ">");
	
	metadata = activeDocument.xmpMetadata.rawData.split("\n");
	result = "";
	for ( i=0; i<metadata.length; i++ ) {
		if ( metadata[i].match(reFind) ) {
			metadata[i] = metadata[i].replace(reStart, "");
			metadata[i] = metadata[i].replace(reEnd, "");
			result = metadata[i];
		}
	}
	
	return result;
}

// -------------------------------------------------------
// leftTrim()
// Utility function to trim whitespace from left of string
// -------------------------------------------------------
Acr.prototype['leftTrim'] = function (str) {
	while (str.substring(0,1) == ' ') {
		str = str.substring(1, str.length);
	}
	return str;
}

// -------------------------------------------------------
// GetActiveDocFileName()
// Returns the Filename of the active document.
// -------------------------------------------------------
Acr.prototype['GetActiveDocFileName'] = function () {
	return activeDocument.fullName;
}

// -------------------------------------------------------
// GetActiveDocWidth()
// Returns the width of the active document.
// -------------------------------------------------------
Acr.prototype['GetActiveDocWidth'] = function () {
	return activeDocument.width;
}

// -------------------------------------------------------
// SetCameraModel()
// -------------------------------------------------------
Acr.prototype['SetCameraModel'] = function (cmod) {
	this.cmod = cmod;
}

// -------------------------------------------------------
// SetFileName()
// -------------------------------------------------------
Acr.prototype['SetFileName'] = function (fname) {
	this.fname = fname;
}

// -------------------------------------------------------
// SetSize()
// -------------------------------------------------------
Acr.prototype['SetSize'] = function (w) {
	this.sz = w;
}

// -------------------------------------------------------
// SetColorTemp()
// -------------------------------------------------------
Acr.prototype['SetColorTemp'] = function (temp) {
	this.temp = parseInt(temp+"");
	if ( isNaN(this.temp) ) {
		this.temp = 5000;
	}
}

// -------------------------------------------------------
// SetTint()
// -------------------------------------------------------
Acr.prototype['SetTint'] = function (tint) {
	this.tint = parseInt(tint+"");
	if ( isNaN(this.tint) ) {
		this.tint = 0;
	}
}

// -------------------------------------------------------
// SetExposure()
// -------------------------------------------------------
Acr.prototype['SetExposure'] = function (exps) {
	this.exps = parseFloat(exps+"");
	if ( isNaN(this.exps) ) {
		this.exps = 0;
	}
}

// -------------------------------------------------------
// SetShadow()
// -------------------------------------------------------
Acr.prototype['SetShadows'] = function (shad) {
	this.shad = parseInt(shad+"");
	if ( isNaN(this.shad) ) {
		this.shad = 0;
	}
}

// -------------------------------------------------------
// SetBrightness()
// -------------------------------------------------------
Acr.prototype['SetBrightness'] = function (brgh) {
	this.brgh = parseInt(brgh+"");
	if ( isNaN(this.brgh) ) {
		this.brgh = 50;
	}
}

// -------------------------------------------------------
// SetContrast()
// -------------------------------------------------------
Acr.prototype['SetContrast'] = function (cntr) {
	this.cntr = parseInt(cntr+"");
	if ( isNaN(this.cntr) ) {
		this.cntr = 25;
	}
}

// -------------------------------------------------------
// SetSaturation()
// -------------------------------------------------------
Acr.prototype['SetSaturation'] = function (strt) {
	this.strt = parseInt(strt+"");
	if ( isNaN(this.strt) ) {
		this.strt = 0;
	}
}

// -------------------------------------------------------
// SetShadowTint()
// -------------------------------------------------------
Acr.prototype['SetShadowTint'] = function (blkb) {
	this.blkb = parseInt(blkb+"");
	if ( isNaN(this.blkb) ) {
		this.blkb = 0;
	}
}

// -------------------------------------------------------
// SetGreenHue()
// -------------------------------------------------------
Acr.prototype['SetGreenHue'] = function (ghue) {
	this.ghue = parseInt(ghue+"");
	if ( isNaN(this.ghue) ) {
		this.ghue = 0;
	}
}

// -------------------------------------------------------
// SetGreenSat()
// -------------------------------------------------------
Acr.prototype['SetGreenSat'] = function (gsat) {
	this.gsat = parseInt(gsat+"");
	if ( isNaN(this.gsat) ) {
		this.gsat = 0;
	}
}

// -------------------------------------------------------
// SetBlueHue()
// -------------------------------------------------------
Acr.prototype['SetBlueHue'] = function (bhue) {
	this.bhue = parseInt(bhue+"");
	if ( isNaN(this.bhue) ) {
		this.bhue = 0;
	}
}

// -------------------------------------------------------
// SetBlueSat()
// -------------------------------------------------------
Acr.prototype['SetBlueSat'] = function (bsat) {
	this.bsat = parseInt(bsat+"");
	if ( isNaN(this.bsat) ) {
		this.bsat = 0;
	}
}

// -------------------------------------------------------
// SetRedHue()
// -------------------------------------------------------
Acr.prototype['SetRedHue'] = function (rhue) {
	this.rhue = parseInt(rhue+"");
	if ( isNaN(this.rhue) ) {
		this.rhue = 0;
	}
}

// -------------------------------------------------------
// SetRedSat()
// -------------------------------------------------------
Acr.prototype['SetRedSat'] = function (rsat) {
	this.rsat = parseInt(rsat+"");
	if ( isNaN(this.rsat) ) {
		this.rsat = 0;
	}
}

// -------------------------------------------------------
// Open()
// Opens RAW file with ACR
// -------------------------------------------------------
Acr.prototype['Open'] = function () {
	var supportedVersions;
	var supported;
	
	this.nOpenCalls++;
	
	var desc1 = new ActionDescriptor();
    desc1.putPath( charIDToTypeID( "null" ), new File( this.fname ) );
	var desc2 = new ActionDescriptor();
	
	// Camera Model
	desc2.putString( charIDToTypeID( "CMod" ), this.cmod );
	
	// Custom Settings
	desc2.putEnumerated( charIDToTypeID( "Sett" ), charIDToTypeID( "Sett" ), charIDToTypeID( "Cst " ) );
	
	// Adjust Tab:
	
	// Custom White Balance
	desc2.putEnumerated( charIDToTypeID( "WBal" ), charIDToTypeID( "WBal" ), charIDToTypeID( "Cst " ) );
	desc2.putInteger( charIDToTypeID( "Temp" ), this.temp );
	desc2.putInteger( charIDToTypeID( "Tint" ), this.tint );
	
	desc2.putDouble( charIDToTypeID( "Exps" ), this.exps );
	desc2.putInteger( charIDToTypeID( "Shad" ), this.shad );
	desc2.putInteger( charIDToTypeID( "Brgh" ), this.brgh );
	desc2.putInteger( charIDToTypeID( "Cntr" ), this.cntr );
	desc2.putInteger( charIDToTypeID( "Strt" ), this.strt );
	
	// Detail Tab:
	
	desc2.putInteger( charIDToTypeID( "Shrp" ), this.shrp );
	desc2.putInteger( charIDToTypeID( "LNR " ), this.lnr );
	desc2.putInteger( charIDToTypeID( "CNR " ), this.cnr );
	
	// Lens Tab:
	
	desc2.putInteger( charIDToTypeID( "CA1 " ), this.ca1 );
	desc2.putInteger( charIDToTypeID( "CA2 " ), this.ca2 );
	desc2.putInteger( charIDToTypeID( "VigA" ), this.viga );
	desc2.putInteger( charIDToTypeID( "VigM" ), this.vigm );
				
	// Calibrate Tab:
				
	desc2.putInteger( charIDToTypeID( "BlkB" ), this.blkb );
	desc2.putInteger( charIDToTypeID( "RHue" ), this.rhue );
	desc2.putInteger( charIDToTypeID( "RSat" ), this.rsat );
	desc2.putInteger( charIDToTypeID( "GHue" ), this.ghue );
	desc2.putInteger( charIDToTypeID( "GSat" ), this.gsat );
	desc2.putInteger( charIDToTypeID( "BHue" ), this.bhue );
	desc2.putInteger( charIDToTypeID( "BSat" ), this.bsat );

	// Settings introduced in ACR 4.0.  Null these out so they don't affect the calibration
	supportedVersions = Array( "4.0",    "4.1" );
	supported = false;
	for (var i = 0; i < supportedVersions.length; i++) {
		if (supportedVersions[i] == this.version) {
			supported = true;
		} 
    }        
	
	if (supported) {
		desc2.putBoolean( charIDToTypeID( "CtoG" ), false );		// Color to Grayscale
		desc2.putInteger( charIDToTypeID( "HRec" ), 0 );			// Highlight Recovery
		desc2.putInteger( charIDToTypeID( "FLit" ), 0 );			// Fill Light
		desc2.putInteger( charIDToTypeID( "Vibr" ), 0 );			// Vibrance
		desc2.putInteger( charIDToTypeID( "HA_R" ), 0 );			// Hue Adjust Reds
		desc2.putInteger( charIDToTypeID( "HA_O" ), 0 );			// Hue Adjust Oranges
		desc2.putInteger( charIDToTypeID( "HA_Y" ), 0 );			// Hue Adjust Yellows
		desc2.putInteger( charIDToTypeID( "HA_G" ), 0 );			// Hue Adjust Greens
		desc2.putInteger( charIDToTypeID( "HA_A" ), 0 );			// Hue Adjust Aquas
		desc2.putInteger( charIDToTypeID( "HA_B" ), 0 );			// Hue Adjust Blues
		desc2.putInteger( charIDToTypeID( "HA_P" ), 0 );			// Hue Adjust Purples
		desc2.putInteger( charIDToTypeID( "HA_M" ), 0 );			// Hue Adjust Magentas
		desc2.putInteger( charIDToTypeID( "SA_R" ), 0 );			// Saturation Adjust Reds
		desc2.putInteger( charIDToTypeID( "SA_O" ), 0 );			// Saturation Adjust Oranges
		desc2.putInteger( charIDToTypeID( "SA_Y" ), 0 );			// Saturation Adjust Yellows
		desc2.putInteger( charIDToTypeID( "SA_G" ), 0 );			// Saturation Adjust Greens
		desc2.putInteger( charIDToTypeID( "SA_A" ), 0 );			// Saturation Adjust Aquas
		desc2.putInteger( charIDToTypeID( "SA_B" ), 0 );			// Saturation Adjust Blues
		desc2.putInteger( charIDToTypeID( "SA_P" ), 0 );			// Saturation Adjust Purples
		desc2.putInteger( charIDToTypeID( "SA_M" ), 0 );			// Saturation Adjust Magentas
		desc2.putInteger( charIDToTypeID( "LA_R" ), 0 );			// Luminance Adjust Reds
		desc2.putInteger( charIDToTypeID( "LA_O" ), 0 );			// Luminance Adjust Oranges
		desc2.putInteger( charIDToTypeID( "LA_Y" ), 0 );			// Luminance Adjust Yellows
		desc2.putInteger( charIDToTypeID( "LA_G" ), 0 );			// Luminance Adjust Greens
		desc2.putInteger( charIDToTypeID( "LA_A" ), 0 );			// Luminance Adjust Aquas
		desc2.putInteger( charIDToTypeID( "LA_B" ), 0 );			// Luminance Adjust Blues
		desc2.putInteger( charIDToTypeID( "LA_P" ), 0 );			// Luminance Adjust Purples
		desc2.putInteger( charIDToTypeID( "LA_M" ), 0 );			// Luminance Adjust Magentas
		desc2.putInteger( charIDToTypeID( "STHH" ), 0 );			// Split Toning Highlights Hue
		desc2.putInteger( charIDToTypeID( "STHS" ), 0 );			// Split Toning Highlights Saturation
		desc2.putInteger( charIDToTypeID( "STB " ), 0 );			// Split Toning Balance
		desc2.putInteger( charIDToTypeID( "STSH" ), 0 );			// Split Toning Shadows Hue
		desc2.putInteger( charIDToTypeID( "STSS" ), 0 );			// Split Toning Shadows Saturation
		desc2.putInteger( charIDToTypeID( "PC_S" ), 0 );			// Parametric Curve Shadows
		desc2.putInteger( charIDToTypeID( "PC_D" ), 0 );			// Parametric Curve Darks
		desc2.putInteger( charIDToTypeID( "PC_L" ), 0 );			// Parametric Curve Lights
		desc2.putInteger( charIDToTypeID( "PC_H" ), 0 );			// Parametric Curve Highlights
		desc2.putInteger( charIDToTypeID( "PC_1" ), 25 );			// Parametric Curve Point 1 Position
		desc2.putInteger( charIDToTypeID( "PC_2" ), 50 );			// Parametric Curve Point 2 Position
		desc2.putInteger( charIDToTypeID( "PC_3" ), 75 );			// Parametric Curve Point 3 Position
		var list1 = new ActionList();
		list1.putInteger( 0 );
		list1.putInteger( 0 );
		list1.putInteger( 255 );
		list1.putInteger( 255 );
        desc2.putList( charIDToTypeID( "Crv " ), list1 );			// Linear Tone Curve
	}
	
	// Settings introduced in ACR 4.1.  Null these out so they don't affect the calibration
	supportedVersions = Array( "4.1" );
	supported = false;
	for (var i = 0; i < supportedVersions.length; i++) {
		if (supportedVersions[i] == this.version) {
			supported = true;
		} 
    }
	if (supported) {
        desc2.putInteger( charIDToTypeID( "Clar" ), 0 );			// Clarity
        desc2.putDouble( charIDToTypeID( "ShpR" ), 1.000000 );		// Sharpening Radius
        desc2.putInteger( charIDToTypeID( "ShpD" ), 25 );			// Sharpening Detail
        desc2.putInteger( charIDToTypeID( "ShpM" ), 0 );			// Sharpening Masking
        desc2.putInteger( charIDToTypeID( "DeFr" ), 0 );			// Lens Corrections Defringe
	}
				
	// Colorspace: ProPhoto RGB
	desc2.putEnumerated( charIDToTypeID( "ClrS" ), charIDToTypeID( "ClrS" ), charIDToTypeID( this.clrs ) );
	// Bit Depth: 8 bits/channel
	desc2.putEnumerated( charIDToTypeID( "BtDp" ), charIDToTypeID( "BtDp" ), charIDToTypeID( this.btdp ) );
	// Size: nearest to 1024 pixels wide
	desc2.putInteger( charIDToTypeID( "Sz  " ), this.sz );
	// Resolution: 240
	desc2.putDouble( charIDToTypeID( "Rslt" ), this.rslt );
	// Resolution Unit: Pixels per Inch
	desc2.putEnumerated( charIDToTypeID( "ReUn" ), charIDToTypeID( "ReUn" ), charIDToTypeID( this.reun ) );
				
	desc1.putObject( charIDToTypeID( "As  " ), stringIDToTypeID( "Adobe Camera Raw" ), desc2 );

	try {
		executeAction( charIDToTypeID( "Opn " ), desc1, DialogModes.NO );
	} catch (e) {
		throw("Incorrect or missing file:\n\n" +
			  "The file:\n     " + this.fname + "\n" +
			  "is either missing or not a camera raw file.\n\n" +
			  "Please open a camera raw file before running the script.");
	}
				
	cc.MarkAllPatchesUnread();
				
	// Increment raw file open counter
	this.nOpens++;
}

// -------------------------------------------------------
// Close()
// Closes active document without saving
// -------------------------------------------------------
Acr.prototype['Close'] = function () {
	this.nCloseCalls++;
	activeDocument.close(SaveOptions.DONOTSAVECHANGES);
}
